package com.litt.saap.core.module.exp;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FilenameUtils;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.litt.core.io.util.FileUtils;
import com.litt.core.io.util.IOUtils;

import freemarker.cache.StringTemplateLoader;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;


/**
 * word导出.
 * 
 * <pre><b>描述：</b>
 *    根据word模板导出word文件，需office2007以上版本，支持WordML
 * </pre>
 * 
 * <pre><b>修改记录：</b>
 *    
 * </pre>
 * 
 * @author <a href="mailto:littcai@hotmail.com">蔡源</a>
 * @since 2015年2月28日
 * @version 1.0
 */
public class WordExpWriter {
  
  private final static Logger logger = LoggerFactory.getLogger(ExcelExtExpWriter.class);
  
  private File targetFile;
  
  /** 数据上下文. */
  private Map<String, Object> context = new HashMap<String, Object>();  
  
  /** 需要动态插入的图片 */
  private File[] imageFiles;
  
 Configuration templateConfig = new Configuration(); 
  
  StringTemplateLoader templateLoader = new StringTemplateLoader();
  
  public WordExpWriter(String targetPath, String targetFileName) throws Exception
  {
    this.targetFile = new File(targetPath, targetFileName);
    if(!targetFile.getParentFile().exists())
    {
      FileUtils.createDirectory(targetFile.getParentFile());
    }
    this.initFreemaker();    
  }
  
  /**
   * Inits the freemaker.
   * 
   * @throws TemplateException the template exception
   * @throws IOException Signals that an I/O exception has occurred.
   * @throws ConfigurationException the configuration exception
   */
  private void initFreemaker() throws TemplateException,IOException,ConfigurationException
  {
    templateConfig = new Configuration();
    templateConfig.setClassForTemplateLoading(this.getClass(), "/");
    templateConfig.setTemplateLoader(templateLoader);
    templateConfig.setClassicCompatible(true);  //允许NULL值
    //templateConfig.setDirectoryForTemplateLoading(new File(projectPath, "template"));
    BeansWrapper wrapper = (BeansWrapper)BeansWrapper.BEANS_WRAPPER;   
    wrapper.setExposureLevel(BeansWrapper.EXPOSE_ALL);
    TemplateHashModel tempStatics = wrapper.getStaticModels();
    PropertiesConfiguration config = new PropertiesConfiguration("freemarkerstatic.properties");
    Iterator keyIterator = config.getKeys();
    while(keyIterator.hasNext())
    {
      String key = keyIterator.next().toString();
      templateConfig.setSharedVariable(key, tempStatics.get(config.getString(key)));
    }   
    // 指定模版如何查看数据模型.
    templateConfig.setObjectWrapper(new DefaultObjectWrapper()); 
        
    this.context.put("static", ((BeansWrapper)templateConfig.getObjectWrapper()).getStaticModels());  
    this.context.put("thread", useClass("java.lang.Thread"));  
    this.context.put("system", useClass("java.lang.System")); 
  }
  
  //拿到静态Class的Model  
  public TemplateModel useClass(String className) throws TemplateModelException  
  {  
      BeansWrapper wrapper = (BeansWrapper) templateConfig.getObjectWrapper();  
      TemplateHashModel staticModels = wrapper.getStaticModels();  
      return staticModels.get(className);  
  }
  
  //拿到目标对象的model  
  public TemplateModel useObjectModel(Object target) throws TemplateModelException  
  {  
      ObjectWrapper wrapper = templateConfig.getObjectWrapper();  
      TemplateModel model = wrapper.wrap(target);  
      return model;         
  }  
    
  //拿到目标对象某个方法的Model    
  public TemplateModel useObjectMethod(Object target, String methodName) throws TemplateModelException  
  {     
      TemplateHashModel model = (TemplateHashModel) useObjectModel(target);  
      return model.get(methodName);         
  }  
  
  public void addData(String name, Object data)
  {
    this.context.put(name, data);
  }
  
  public void addData(Map<String, Object> data)
  {
    this.context.putAll(data);
  }
  
  public File build(File templateFile) throws IOException, InvalidFormatException
  {
    String fileName = FilenameUtils.getBaseName(templateFile.getName());
    return this.build(fileName, new FileInputStream(templateFile));
  }
  
  public File build(String templateName, InputStream templateInputStream) throws IOException, InvalidFormatException
  {
    /*
     * 这里利用OPCPackage直接将docx作为一个zip文件打开，并获取到document.xml正文，然后通过freemarker做内容转换
     */
    //OPCPackage opcPackage = POIXMLDocument.openPackage(templateFile.getPath());
    OPCPackage opcPackage = OPCPackage.open(templateInputStream);
    
    List<PackagePart> parts = opcPackage.getParts();
    
    for (PackagePart packagePart : parts)
    {
      if("/word/document.xml".equals(packagePart.getPartName().getName()))
      {
        String contentType = packagePart.getContentType();
        OutputStream outputStream = packagePart.getOutputStream();
        //读取并转换
        
        StringWriter stringWriter = new StringWriter();
         
        IOUtils.copy(packagePart.getInputStream(), stringWriter);
        String fileContent = stringWriter.toString();
        logger.debug("Word content:{}", fileContent);
        StringTemplateLoader stringTemplateLoader = (StringTemplateLoader)templateConfig.getTemplateLoader();
        stringTemplateLoader.putTemplate(templateName, fileContent);    
        
        Template template = templateConfig.getTemplate(templateName);
        template.setEncoding(contentType);
        
        
        Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        try
        {
          template.process(this.context, writer);
          writer.flush();
        } catch (TemplateException e)
        {
          throw new IllegalStateException("Can't process template parser.", e);
        }
        finally
        {
          IOUtils.closeQuietly(writer);
        }
        
        //把document.xml写到文件里，可用于测试
//        File tmpFile = new File(targetFile.getParent(), FilenameUtils.getBaseName(targetFile.getName())+"-document.xml");   
//        IOUtils.copy(packagePart.getInputStream(), new FileOutputStream(tmpFile));
      }
    }
    opcPackage.save(targetFile);    
    return targetFile;
  }
      
  public static void main(String[] args) throws Exception
  {    
    //XWPFDocument wordDocument = new XWPFDocument(new FileInputStream(new File("d:\\SH-SF02-t.xml")));
    //POIXMLDocument.openPackage(new File("d:\\SH-SF02-t.xml").getPath());
    WordXmlExpWriter writer = new WordXmlExpWriter("d:\\", "SH-SF02-t.docx");
    
    
    writer.addData("orderDate", new Date());
    writer.addData("totalNum", 456);
    writer.build(new File("d:\\SH-SF02.docx"));
  }
}
